<?php

namespace App\RedisAgent;

use App\Constants\ErrorCode;
use App\Constants\RedisKey;
use App\Exception\CommonException;
use App\Service\ToolService;
use Hyperf\Context\ApplicationContext;
use Hyperf\Redis\RedisFactory;
use Hyperf\Redis\RedisProxy;

class SmsAgent {
    private RedisProxy $redisClient;
    private string     $keyPre;
    private int        $top   = 10;
    private int        $limit = 60;

    public function __construct() {
        $this->keyPre      = RedisKey::PREFIX . ":SmsAgent:";
        $this->redisClient = ApplicationContext::getContainer()->get(RedisFactory::class)->get('db_2');
    }

    /**
     * 获取验证码
     * @param $mobile
     * @return string
     */
    public function getCode($mobile): string {
        $this->checkLimit($mobile);
        $this->check($mobile);

        $code = ToolService::randString(6, 1);
        $this->redisClient->set($this->keyPre . $mobile, $code, $this->limit * 5);
        $this->redisClient->set($this->keyPre . $mobile . "Limit", 1, $this->limit);
        $this->redisClient->hIncrBy($this->keyPre . "top", $mobile, 1);

        return $code;
    }

    /**
     * 校验验证码
     * @param $mobile
     * @param $code
     * @return void
     */
    public function checkCode($mobile, $code): void {
        if (!$this->redisClient->exists($this->keyPre . $mobile)) {
            throw new CommonException("验证码不正确", ErrorCode::NOT_EXISTS);
        }
        $codeOld = $this->redisClient->get($this->keyPre . $mobile);
        if ($codeOld != $code) {
            throw new CommonException("验证码不正确", ErrorCode::NOT_EXISTS);
        }
        $this->redisClient->del($this->keyPre . $mobile);
    }

    /**
     * 检测发送频率
     * @param $mobile
     * @return void
     */
    private function checkLimit($mobile): void {
        if ($this->redisClient->exists($this->keyPre . $mobile . "Limit")) {
            $ttl = $this->redisClient->ttl($this->keyPre . $mobile . "Limit");
            throw new CommonException("请勿频繁发送|{$ttl}", ErrorCode::SMS_CODE_TIME_LIMIT);
        }
    }

    /**
     * 检测日发送上限
     * @param $mobile
     * @return void
     */
    private function check($mobile): void {
        if (!$this->redisClient->exists($this->keyPre . "top")) {
            $this->redisClient->hSet($this->keyPre . "top", $mobile, 0);
            $end = strtotime(date('Y-m-d 23:59:59')) + 1;
            $this->redisClient->expireAt($this->keyPre . "top", $end);
        } else {
            $send = (int)$this->redisClient->hGet($this->keyPre . "top", $mobile);
            if ($send >= $this->top) {
                throw new CommonException("今日已达发送上限，请次日在试", ErrorCode::NUMBER_MATCH_ERROR);
            }
        }
    }
}
